﻿#!/usr/bin/env python
#coding:utf-8
import sys
import struct
import socket,errno
import hashlib
import random
import select
import ctypes
from   ctypes import *
import time
import datetime
import re
import platform

reload(sys)
sys.setdefaultencoding('utf-8')
sys.path.append("../zspy_base/")	# 需要在使用 configer和proto之前，导入基础部分文件的路径

import comm.configer as configer
import comm.common_tools as common_tools

import proto.zsapp_im_msg_head_pb2      as head_proto
import proto.zsapp_im_msg_register_pb2  as register_proto    
import proto.zsapp_im_msg_login_pb2     as login_proto
import proto.zsapp_im_login_notify_pb2  as login_notify
import proto.zsapp_im_c2c_msg_info_pb2	as c2c_msg_info
import proto.zsapp_im_revoke_c2c_msg_info_pb2	as revoke_c2c_msg_info
import proto.zsapp_im_group_msg_info_pb2  as group_msg_info
import proto.zsapp_im_revoke_group_msg_info_pb2  as revoke_group_msg_info
import proto.zsapp_im_get_group_id_list_pb2  as get_group_id_list
import proto.zsapp_im_create_group_pb2  as create_group
import proto.zsapp_im_set_info_pb2      as set_info
import proto.zsapp_im_group_switch_setting_pb2	as group_switch_setting
import proto.zsapp_im_push_conf_pb2	    as offline_push_proto
import proto.zsapp_im_c2c_msg_read_pb2	as c2c_msg_read
import proto.zsapp_im_c2c_msg_recvd_pb2	as c2c_msg_recvd
import proto.zsapp_im_apply_for_group_pb2		as apply_for_group
import proto.zsapp_im_delete_group_member_pb2	as delete_group_member
import proto.zsapp_im_active_abort_group_pb2	as abort_group
import proto.zsapp_im_invite_to_group_pb2		as invite_group


# 模拟前端app的信息
class CAppInfo:
	def __init__(self):
		self.pic_verify_key = "34v7#9.(#8UVguSQ"
		self.int32_version = 0x20207
		self.app_version = '1.5.8'
		self.os_info = 'android_6.0.1'
		self.build_info = 'Build1234'
		self.os_version = '%s|V%s|%s' % (self.os_info, self.app_version, self.build_info)
		#self.passwd_md5_str = '97f2df5357c420751d0c78413fa84641'
		self.phone_num = ''
		self.password = 'zs123456'
		self.cookie_sig = ''
		self.str_cookie_key = ''
		self.self_uid = 0
		self.addr_link = []	# 本机地址
		self.addr_peer = []	# 服务器地址

app_info = CAppInfo()

BUFFSIZE = 4096


#利用协议打包
def package_format(head, body, key = None):
	if head == None:
		return None
	
	head_str = head.SerializeToString()
	head_str_len = len(head_str)
	
	body_str_len = 0
	body_str = ""
	if body != None:
		body_str = body.SerializeToString()
		if key != None:
			out_buf = create_string_buffer(len(body_str) + 17);
			out_buf_len = c_int(len(body_str) + 17)	
			configer.oi_tea.oi_symmetry_encrypt2(c_char_p(body_str), c_int(len(body_str)), c_char_p(key), out_buf, pointer(out_buf_len))
			body_str_len = out_buf_len.value
			body_str = out_buf.raw[:body_str_len]
			#body_str = out_buf.value		
		else:
			body_str_len = len(body_str)		
			
	data_len = 12 + head_str_len + body_str_len
	fmt = '!3i%ds%ds' % (head_str_len ,body_str_len)	
	data = struct.pack(fmt, data_len, head_str_len, body_str_len, head_str, body_str)
	#print 'data_len',data_len
	return data,data_len

#工厂模式
def proto_factory(maincmd):
	if maincmd == None or maincmd == 0x606:
		return None
	elif maincmd == 0:
		return head_proto.Head()
	elif maincmd == 0x102:
		return register_proto.get_register_page_rsp()
	elif maincmd == 0x103 or maincmd == 0x303:
		return register_proto.register_getsms_num_req()
	elif maincmd == 0x105 or maincmd == 0x305:
		return register_proto.register_req()
	elif maincmd == 0x106 or maincmd == 0x306:
		return register_proto.register_rsp()
	elif maincmd == 0x107 or maincmd == 0x307:
		return register_proto.register_set_passwd_req()
	elif maincmd == 0x121:
		return login_proto.login_pw_request()
	elif maincmd == 0x122:
		return login_proto.login_pw_rsp()
	elif maincmd == 0x123:
		return login_proto.cookie_login_req()
	elif maincmd == 0x124:
		return login_proto.cookie_login_rsp()
	elif maincmd == 0x125:
		return login_notify.login_completion_nofity_req()
	elif maincmd == 0x126:
		return login_notify.login_completion_nofity_rsp()
	elif maincmd == 0x131:
		return c2c_msg_info.send_msg()
	elif maincmd == 0x132:
		print 'c2c_msg_info.send_msg_ack()', c2c_msg_info.send_msg_ack()
		return c2c_msg_info.send_msg_ack()
	elif maincmd == 0x134 or maincmd == 0x502 or maincmd == 0x504 or maincmd == 0x506 or maincmd == 0x508 or maincmd == 0x522:
		return c2c_msg_info.push_msg()
	elif maincmd == 0x133 or maincmd == 0x503 or maincmd == 0x505 or maincmd == 0x507 or maincmd == 0x509 or maincmd == 0x523:
		return c2c_msg_info.push_msg_ack()
	elif maincmd == 0x161:
		return revoke_c2c_msg_info.revoke_msg()
	elif maincmd == 0x164:
		return revoke_c2c_msg_info.revoke_push_msg()
	elif maincmd == 0x163:
		return revoke_c2c_msg_info.revoke_push_msg_ack()
	elif maincmd == 0x221:
		return set_info.SetBaseInfoReq()
	elif maincmd == 0x222:
		return set_info.SetBaseInfoRsp()
	elif maincmd == 0x8A1 or maincmd == 0x8A6:
		return group_msg_info.send_group_msg()
	elif maincmd == 0x8A2:
		return group_msg_info.send_group_msg_ack()
	elif maincmd == 0x8B1:
		return revoke_group_msg_info.recall_group_msg()
	elif maincmd == 0x8B2:
		return revoke_group_msg_info.recall_group_msg_ack()
	elif maincmd == 0x8A4:
		return group_msg_info.send_group_msg_notify()

	elif maincmd == 0x821:	return apply_for_group.apply_for_group_req()
	elif maincmd == 0x824:	return apply_for_group.apply_for_group_admin_req()
	elif maincmd == 0x827:	return apply_for_group.apply_for_group_admin_rsp()
	elif maincmd == 0x828:	return apply_for_group.apply_for_group_admin_rsp_ack()
	elif maincmd == 0x82A:	return apply_for_group.apply_for_group_result()
	elif maincmd == 0x82C:	return apply_for_group.apply_for_group_notify()
	elif maincmd == 0x841:	return delete_group_member.delete_group_member_req()
	elif maincmd == 0x844:	return delete_group_member.delete_group_member_notify()
	elif maincmd == 0x847:	return abort_group.active_abort_group_req()
	elif maincmd == 0x84A:	return abort_group.active_abort_group_req_notify()
	elif maincmd == 0x831:	return invite_group.invite_to_group_req()
	elif maincmd == 0x832:	return invite_group.invite_to_group_rsp()
	elif maincmd == 0x834:	return invite_group.invite_to_group_notify()

# 发送群配置开关
	elif maincmd == 0x85B or maincmd == 0x85C or maincmd == 0x85E or maincmd == 0x85F:
		return group_switch_setting.group_switch_setting()
		
# 获取群ID
	elif maincmd == 0x881:
		return get_group_id_list.get_group_id_list_req()
	elif maincmd == 0x882:
		return get_group_id_list.get_group_id_list_rsp()

# 创建群
	elif maincmd == 0x801:
		return create_group.create_group_id_req()
	elif maincmd == 0x802:
		return create_group.create_group_id_rsp()
	elif maincmd == 0x803:
		return create_group.create_group_req()
	elif maincmd == 0x804:
		return create_group.create_group_rsp()
	# 客户端离线消息推送配置
	elif maincmd == 0x151:
		return offline_push_proto.offline_push_config()

	elif maincmd == 0x174:
		return c2c_msg_recvd.recvd_push_msg()
	elif maincmd == 0x178:
		return c2c_msg_recvd.recvd_push_msgs()
	elif maincmd == 0x184:
		return c2c_msg_read.read_push_msg()
	elif maincmd == 0x188:
		return c2c_msg_read.read_push_msgs()

	return None
	
	
	
#根据proto协议分析数据
def parse_package(data, maincmd, key = None):
	if data == None:
		return None,None		
	if 0 == len(data):
		print 'parse_package error!!!'
		print "\t maincmd=0x%02x" % maincmd
		print '\t data\'s length is: ', len(data)
		return None,None		

	len_array = struct.unpack('!3i',data[:struct.calcsize('3i')])	
	head_p = head_proto.Head()
	head_p.ParseFromString(data[12:12 + len_array[1]])
	#print 'total_len:%d, head_len:%d, body_len:%d' % (len_array[0], len_array[1], len_array[2])

	maincmds = head_p.msg_cshead.int32_maincmd
	if maincmd != None and maincmd != 0 and maincmd != maincmds:
		print '\n\n!!!! I want cmd is 0x%02x, but in pkg is 0x%02x!!!\n\n' % (maincmd, maincmds)

	#print 'maincmd:0x%x, total_len:%d, head_len:%d, body_len:%d' % (head_p.msg_cshead.int32_maincmd, len_array[0], len_array[1], len_array[2])
	body_p = proto_factory(maincmds)
	if body_p == None:		
		return head_p, None	
	else:		
		if key == None:
			body_p.ParseFromString(data[12 + len_array[1]:])
		else:
			try:
				body_en_str = data[12 + len_array[1]:]
				out_buf = create_string_buffer(len_array[2])
				out_buf_len = c_int(len_array[2])
			
				ret = configer.oi_tea.oi_symmetry_decrypt2(c_char_p(body_en_str), c_int(len_array[2]), c_char_p(key), out_buf, pointer(out_buf_len))
				if ret == 0:
					print 'parse_package: oi_symmetry_decrypt2 fail!!!!! ',
					print '\tbody_en_str: ', body_en_str,
					print '\tlength: ', len_array[2],
					print '\tkey: ', key
					return head_p, None
				body_p.ParseFromString(out_buf.raw[:out_buf_len.value]) 
			except Exception,e:					
				body_p.ParseFromString(data[12 + len_array[1]:])
	
	return head_p, body_p

def get_verify_code_from_db(phonenum):

	sqlstr = 'SELECT verify_code FROM table_sms_verify_code WHERE phone_num = %s' % phonenum

	mysql_db = configer.get_im_mysql_conn()
	cursor = mysql_db.cursor()

	try:
		cursor.execute(sqlstr)
		result = cursor.fetchone()
		if result is None:
			return 1, "can't find verify code!!"
		return 0, result[0]

	except :
		print "EXCEPT!!! sql = %s" % sql;
		return 2, "access database failed!!"


def get_common_head():
	head = head_proto.Head();
	head.int32_module_id = 0x01
	head.int32_head_type = 0x01

	cshead = head.msg_cshead
	cshead.int32_version = app_info.int32_version
	cshead.int32_from_uid = app_info.self_uid
	cshead.int32_msg_seq = random.randint(10000000, 99999999)
	cshead.uint32_retry_times = 0
	cshead.uint32_session_id = 0
	cshead.client_ip = app_info.addr_link[0]
	cshead.client_port = app_info.addr_link[1]
	cshead.str_os_version = app_info.os_version

	return head;
	



	
#注册和找回密码功能流程
def register_and_find_passwd_process(tcp_sock, cmd_map, phone_num, password):

	self_uid = 0

	head = get_common_head()
	cshead = head.msg_cshead
	cshead.int32_from_uid = self_uid	# 清 uid
	cshead.int32_maincmd = cmd_map['0xX01']	
	data, data_len = package_format(head, None)	
	tcp_sock.send(data)
	#print '-------------------->0x%x success' % cmd_map['0xX01']

	data = tcp_sock.recv(BUFFSIZE)	
	head_p, body_p = parse_package(data, cmd_map['0xX02'])  #或者用一个返回值接收，然后分别进行判断。
	#解密获取图片验证码签名
	pic_code_sig     = body_p.bytes_valide_code_sig
	pic_code_sig_len = len(body_p.bytes_valide_code_sig)	
	img_struct_p = create_string_buffer(72);
	img_struct_len = c_int(72)	
	ret = configer.oi_tea.oi_symmetry_decrypt2(c_char_p(pic_code_sig), c_int(pic_code_sig_len), c_char_p(app_info.pic_verify_key), pointer(img_struct_p), pointer(img_struct_len))
	if ret == 0:
		return 1, 'oi_symmetry_decrypt2 fail !'

	
	data = struct.unpack('=HIIIII50s', img_struct_p)
	#print 'sVerifyString',data[6],type(data[6]),len(data[6][:data[5]])
	pic_verify_code = data[6][:data[5]]
	return_code = head_p.msg_cshead.uint32_result_code	
	if return_code != 0:
		return 2, '0x101 register/find passwd fail, return code = %d' % return_code

	#print '<--------------------0x%x success' % cmd_map['0xX02']
	#register/find passwd second step 0x103-----------------------------
	cshead.int32_maincmd = cmd_map['0xX03']
	register_getsms = proto_factory(cmd_map['0xX03'])
	if register_getsms == None:
		return 3, 'register_getsms instance fail'

	
	register_getsms.str_phone_num = phone_num
	register_getsms.str_valide_code = pic_verify_code
	register_getsms.bytes_valide_code_sig = pic_code_sig	
	data, data_len = package_format(head, register_getsms)
	tcp_sock.send(data)
	#print '-------------------->0x%x success' % cmd_map['0xX03']
	data = tcp_sock.recv(BUFFSIZE)	
	head_p, body_p = parse_package(data, cmd_map['0xX04']) #一个函数应对很多种情况
	return_code = head_p.msg_cshead.uint32_result_code	
	if return_code == 2:
		return 4, '0x103 phone num has already registered, return code = %d' % return_code
	elif return_code != 0:
		return 4, '0x103 register/find passwd fail, return code = %d' % return_code

	#print '<--------------------0x%x success' % cmd_map['0xX04']
	#register/find passwd three step 0x105----------------------------------
	cshead.int32_maincmd = cmd_map['0xX05']
	register_req = proto_factory(cmd_map['0xX05'])
	if register_req == None:
		return 5, 'register_req instance fail'

	register_req.str_phone_num = phone_num
	if cshead.int32_maincmd == 0x105:
		register_req.reg_chan_info.str_phone_num        = phone_num			# 电话号码
		register_req.reg_chan_info.str_channel_id       = 'Channel_python'	# 渠道号
		#register_req.reg_chan_info.str_imei             = 3;  // IMEI国际移动通信设备识别码, iOS 用户填写UDID
		register_req.reg_chan_info.str_channel_name     = 'Channel_python'	# 渠道包名
		register_req.reg_chan_info.str_os               = platform.system()	# 操作系统, Android/iOS
		register_req.reg_chan_info.str_model            =  platform.node()	# 机型, iPhone ...
		#register_req.reg_chan_info.str_mac_addr         = 7;  // mac地址
		register_req.reg_chan_info.str_ip_addr          = '127.0.0.1'		# ip地址
		#register_req.reg_chan_info.str_activation_code  = 9;  // 激活码
		register_req.reg_chan_info.str_app_version      = app_info.app_version	# App版本号
		#register_req.reg_chan_info.str_invitation_code  = 11; // 邀请码


	'''
	if phone_num[:3] == '144' and configer.run_type != 4:
		note_msg = phone_num[:3] + phone_num[8:]	
	else:
		note_msg = raw_input("please input your verify code: ")
	'''
	ret, note_msg = get_verify_code_from_db(phone_num)
	if 0 != ret:
		#note_msg = '5A5357'
		return 9, note_msg

	register_req.str_secure_num = note_msg	
	data, data_len = package_format(head, register_req)
	tcp_sock.send(data)
	#print '-------------------->0x%x success' % cmd_map['0xX05']
	data = tcp_sock.recv(BUFFSIZE)	
	head_p, body_p = parse_package(data, cmd_map['0xX06']) #一个函数应对很多种情况
	if body_p is None:
		return 6, 'parse_package fail,maincmd:0x%x' % cmd_map['0xX06']

	self_uid = body_p.int32_uid
	#print 'user uid:',self_uid
	return_code = head_p.msg_cshead.uint32_result_code	
	if return_code != 0:
		return 7, '0x105 register/find passwd fail, return code = %d' % return_code

	#print '<--------------------0x%x success' % cmd_map['0xX06']
	#register/find passwd four step 0x107-------------------------------------
	cshead.int32_maincmd = cmd_map['0xX07']
	cshead.int32_from_uid = self_uid
	cshead.str_phone_num = phone_num
	register_set_passwd = proto_factory(cmd_map['0xX07'])
	register_set_passwd.int32_uid = self_uid
	register_set_passwd.str_phone_num = phone_num
	#register_set_passwd.bytes_passwd_md5_salt = common_tools.get_twice_md5(password)
	
	p = re.compile(r'(?!^[0-9]+$)(?!^[A-z]+$)(?!^[^A-z0-9]+$)^.{6,16}$')
	match = p.search(password)
	if match:
		register_set_passwd.bytes_passwd_md5_salt = common_tools.get_twice_md5(password)
		print match.group()
	else:
		return 10, 'password is invalid '


	set_passwd_key = note_msg + "ZuoShou@@@"	
	data, data_len = package_format(head, register_set_passwd, set_passwd_key)
	tcp_sock.send(data)
	#print '-------------------->0x%x success' % cmd_map['0xX07']
	data = tcp_sock.recv(BUFFSIZE)	
	head_p, body_p = parse_package(data, cmd_map['0xX08']) #一个函数应对很多种情况	
	return_code = head_p.msg_cshead.uint32_result_code	
	if return_code != 0:
		return 8, '0x107 register fail, return code = %d' % return_code

	#print '<--------------------0x%x success' % cmd_map['0xX08']

	app_info.self_uid		= self_uid	
	app_info.phone_num		= phone_num
	app_info.password		= password

	return 0, "success"


#cookie sig结构体的类表示	
class login_sig:
	def __init__(self):
		self.wTGTGTVer = 0      #2
		self.dwRandom  = 0      #4
		self.Uid  = 0           #4
		self.dwAppID = 0        #4
		self.dwInitTime = 0     #4
		self.dwAppClientVer = 0 #4
		self.dwClientIP  = 0    #4
		self.randomKey = ""     #16


# 创建网络连接
def func_create_socket(ip, port):
	global app_info;

	addr_im_server = (ip, port)
	tcp_sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
	tcp_sock.connect(addr_im_server)

	app_info.addr_peer = tcp_sock.getpeername();
	app_info.addr_link = tcp_sock.getsockname();

	return tcp_sock


# 注册流程
def func_register(tcp_sock, phone_num, password) :
	register_cmd_map = {"0xX01":0x101, "0xX02":0x102, "0xX03":0x103, "0xX04":0x104, "0xX05":0x105, "0xX06":0x106,"0xX07":0x107, "0xX08":0x108}
	return register_and_find_passwd_process(tcp_sock, register_cmd_map, phone_num, password);


# 密码找回	
def func_find_password(tcp_sock, phone_num, password):
	find_passwd_cmd_map = {"0xX01":0x101, "0xX02":0x102, "0xX03":0x303, "0xX04":0x304, "0xX05":0x305, "0xX06":0x306,"0xX07":0x307, "0xX08":0x308}
	return register_and_find_passwd_process(tcp_sock, find_passwd_cmd_map, phone_num, password);




# 密码登录流程
#	end_type -- 1:密码登录就完成了（发送0x125）；2:后续还有cookie登录（不能发送0x125，留在cookie登录在发)
def func_login_password(tcp_sock, phone_num, password, end_type = 1):
	
	global app_info;

	#print "\n******开始密码登录流程******";
	self_uid = 0;		# 0x121不能带uid，必须为0
	head = get_common_head();
	cshead = head.msg_cshead
	cshead.str_phone_num = phone_num
	#scret login
	cshead.int32_maincmd = 0x121
	cshead.int32_from_uid = self_uid	#必须重新写0，否则etcp-interface检查不通过
	head.msg_login_sig.int32_sig_type = 1
	scret_login =  proto_factory(0x121)
	scret_login.int32_uid = self_uid
	scret_login.str_phone_num = phone_num;
	
	login_sig_item = login_sig()
	login_sig_item.wTGTGTVer = 1
	login_sig_item.dwRandom = 8888
	login_sig_item.Uid = self_uid
	login_sig_item.dwAppID = 1
	login_sig_item.dwInitTime = time.time()
	login_sig_item.dwAppClientVer = app_info.int32_version
	login_sig_item.dwClientIP = struct.unpack('i',socket.inet_pton(socket.AF_INET, "127.0.0.1"))[0]
	login_sig_item.randomKey = "0123456789abcdef"
	

	#序列化
	login_sig_str = struct.pack('!HIIIIII16s',  login_sig_item.wTGTGTVer, login_sig_item.dwRandom ,   \
												login_sig_item.Uid, login_sig_item.dwAppID,       \
												login_sig_item.dwInitTime, login_sig_item.dwAppClientVer, \
												login_sig_item.dwClientIP, login_sig_item.randomKey)
	
	#使用密码的2次MD5值作为key，进行加密，生成“登录签名”
	passwd_md5_str = common_tools.get_twice_md5(password)
	out_buf = create_string_buffer(len(login_sig_str) + 17);
	out_buf_len = c_int(len(login_sig_str) + 17)	
	configer.oi_tea.oi_symmetry_encrypt2(c_char_p(login_sig_str), c_int(len(login_sig_str)), c_char_p(passwd_md5_str[:16]), out_buf, pointer(out_buf_len))

	#print '随机密钥是: ', login_sig_item.randomKey;
	#print '登录密码是: ', password;
	#print '两次MD5后 : ', passwd_md5_str;


	# 将“登录签名”放在head部分
	head.msg_login_sig.bytes_sig = out_buf.raw[:out_buf_len.value]
	
	# 使用“随机数”，对消息体进行加密。“随机数”被加密后位于消息头的“登录签名”中
	data, data_len = package_format(head, scret_login, login_sig_item.randomKey)
	if data == None:
		print 'package_format fail'
		return 1, '0x121命令编码错误'
	tcp_sock.send(data)
	#print '-------------------->0x121 success'
	data = tcp_sock.recv(BUFFSIZE)	
	head_p, body_p = parse_package(data, 0x122, login_sig_item.randomKey) 		#一个函数应对很多种情况	
	if head_p == None or body_p == None:
		return 2, 'phonenum+passwd login fail, parse_package error(maincmd=0x122)'

	return_code = head_p.msg_cshead.uint32_result_code	
	if return_code != 0:
		print '0x121 scret login fail,return code,', return_code
		return 3, '0x122命令返回码非零 [%d]' % return_code
	#print '<--------------------0x122 success'

	# 保存cookie和uid信息
	str_cookie_key	= body_p.str_cookie_key
	cookie_sig		= body_p.bytes_cookie_sig
	self_uid		= body_p.int32_uid;

	#print 'cookie_sig: ', repr(cookie_sig);
	#print 'str_cookie_key: ', str_cookie_key;
	#print '我的uid: ', self_uid, type(self_uid)

	#print "******密码登录成功******\n";


	app_info.str_cookie_key	= str_cookie_key
	app_info.cookie_sig		= cookie_sig
	app_info.self_uid		= self_uid
	app_info.phone_num		= phone_num
	app_info.password		= password


	if end_type != 1:
		return 0, "success";


	# 修改登录密钥为 cooike sig
	head.msg_cshead.int32_maincmd = 0x125
	head.msg_cshead.int32_from_uid = self_uid
	head.msg_login_sig.int32_sig_type = 2
	head.msg_login_sig.bytes_sig = cookie_sig

	login_notify = proto_factory(0x125)
	login_notify.int32_uid = self_uid
	login_notify.type = 2	#PASSSWORD_LOGIN_COMPLETION  = 2;  // 密码登录完成
	data, data_len = package_format(head, login_notify, str_cookie_key)
	if data == None:
		print 'package_format fail'
		return 1, '0x125命令编码错误'
	tcp_sock.send(data)
	#print '-------------------->0x125 success'


	data = tcp_sock.recv(BUFFSIZE)	
	head_p, body_p = parse_package(data, 0x126)		 #一个函数应对很多种情况	
	if head_p == None:
		print 'parse_package failed!!'
		return 2, '0x126命令解码错误'

	return_code = head_p.msg_cshead.uint32_result_code	
	if return_code != 0:
		return 3, '0x126命令返回码非零 [%d]' % return_code
	#print '<--------------------0x126 success'
	#print "******客户端确认登录完成******\n";

	return 0, "success";


# cookie 登录流程
def func_login_cookie(tcp_sock, appinfo):

	#print "\n******开始cookie登录流程******";

	cookie_sig		= appinfo.cookie_sig;
	str_cookie_key	= appinfo.str_cookie_key
	self_uid		= appinfo.self_uid
	phone_num		= appinfo.phone_num

	#print 'cookie_sig\'s length:', len(cookie_sig)
	#print 'str_cookie_key:',str_cookie_key
	#print 'uid:',self_uid

	head = get_common_head()
	head.msg_cshead.int32_maincmd = 0x123
	head.msg_cshead.str_phone_num = phone_num;

	head.msg_login_sig.int32_sig_type = 2
	head.msg_login_sig.bytes_sig = cookie_sig

	cookie_login = proto_factory(0x123)
	cookie_login.int32_uid = self_uid
	cookie_login.bytes_cookie_sig = cookie_sig
	cookie_login.bytes_device_token = '12345678901234567891234567890012'
	cookie_login.bytes_getui_clientid = '12345678901234567890123456789012'
	cookie_login.int32_push_type = 1

	data, data_len = package_format(head, cookie_login, str_cookie_key)
	if data == None:
		print 'package_format fail'
		return 1, '0x123命令编码错误'
	tcp_sock.send(data)
	#print '-------------------->0x123 success'

	while True:
		data = tcp_sock.recv(BUFFSIZE)	
		head_p, body_p = parse_package(data, 0x124, str_cookie_key)		 #一个函数应对很多种情况	
		if head_p == None:
			print 'parse_package failed!!'
			return 2, '0x124命令解码错误'
		if head_p.msg_cshead.int32_maincmd == 0x124:
			break;


	return_code = head_p.msg_cshead.uint32_result_code	
	if return_code != 0:
		print '0x123 cookie login fail,return code,', return_code
		return 2, '0x124命令返回码非零 [%d]' % return_code
	#print '<--------------------0x124 success'
	#print "******cookie登录成功******\n";


	head.msg_cshead.int32_maincmd = 0x125
	login_notify = proto_factory(0x125)
	login_notify.int32_uid = self_uid
	login_notify.type = 3	#COOKIE_SIGNATURE_COMPLETION = 3;  // cookie登录完成
	data, data_len = package_format(head, login_notify, str_cookie_key)
	if data == None:
		print 'package_format fail'
		return 1, '0x125命令编码错误'
	tcp_sock.send(data)
	#print '-------------------->0x125 success'


	data = tcp_sock.recv(BUFFSIZE)	
	head_p, body_p = parse_package(data, 0x126)
	if head_p == None:
		print 'parse_package failed!!'
		return 2, '0x126命令解码错误'

	return_code = head_p.msg_cshead.uint32_result_code	
	if return_code != 0:
		print '0x125 客户端确认登录完成失败,return code,', return_code
		return 2, '0x126命令返回码非零 [%d]' % return_code
	#print '<--------------------0x126 success'
	#print "******客户端确认登录完成******\n";
	return 0, "success";

# 登陆完成之后，需要调用这个函数接收0x134消息
def func_get_push_msg(tcp_sock):
	data = tcp_sock.recv(BUFFSIZE)	
	head_p, body_p = parse_package(data, 0, app_info.str_cookie_key)
	if head_p == None or body_p == None:
		return 2, 'func_get_push_msg fail, parse_package error(maincmd=0x222)'

	elif head_p.msg_cshead.int32_maincmd == 0x134:
		'''
		print "RECV From=[ %d ]. seq=%d " % (head_p.msg_cshead.int32_from_uid, head_p.msg_cshead.int32_msg_seq) ,
		msg_subtype = body_p.msg_recv_msg.int32_msg_subtype
		if msg_subtype == 0:
			msg_txt = body_p.msg_recv_msg.msg_txt_attr.msg_content
			print "文本消息：%s" % ( msg_txt)
		elif msg_subtype == 1:
			msg_pic_attr = body_p.msg_recv_msg.msg_pic_attr
			print "图片消息：%s" % (msg_pic_attr.str_pic_url)
		elif msg_subtype == 5:
			msg_location_attr = body_p.msg_recv_msg.msg_location_attr
			print "位置消息：lat:%s, lon:%s" % (msg_location_attr.str_latitude, msg_location_attr.str_longitude)
		elif msg_subtype == 6:
			msg_location_attr = body_p.msg_recv_msg.msg_location_attr
			print "实时位置：(act=%d) lat:%s, lon:%s" % (
				msg_location_attr.int32_action, msg_location_attr.str_latitude, msg_location_attr.str_longitude)
		elif msg_subtype == 8:
			msg_txt = body_p.msg_recv_msg.msg_txt_attr.msg_content
			print "Ｈ５消息：%s" % ( msg_txt)
		else :
			print "其他消息：type=%d" % (head_p.msg_cshead.int32_from_uid, msg_subtype)
		'''
		head_p.msg_cshead.int32_maincmd = 0x133 							
		body = proto_factory(head_p.msg_cshead.int32_maincmd);
		data, data_len = package_format(head_p, body)
		# 发送应答包
		if data_len != 0:
			send_len = 0
			while True:
				send_len += tcp_sock.send(data[send_len:])
				if send_len != len(data):
					continue
				break

		return 0, 'success'


def func_logout(tcp_sock) :

	#print "\n******开始登出流程******";

	head = get_common_head()
	cshead = head.msg_cshead
	cshead.int32_maincmd = 0x605
	data, data_len = package_format(head, None)	
	if data is None:
		print 1, 'package_format fail'

	tcp_sock.send(data)
	#print '-------------------->0x%x success' % 0x605
	data = tcp_sock.recv(BUFFSIZE)	

	head_p, body_p = parse_package(data, 0x606)
	return_code = head_p.msg_cshead.uint32_result_code	
	if return_code != 0:
		return 3, '0x606 命令返回码非零 [%d]' % return_code

	#print '<--------------------0x%x success' % 0x606
	#print "******开始登出流程******\n";

	app_info.self_uid = 0	# 清 uid

	return 0, 'success'







# 设置用户信息。如果外部没有传递set_user_info参数(=None)，则内部用缺省值填写
def func_set_user_base_profile(tcp_sock, app_info, set_user_info = None):
	head = get_common_head()
	cshead = head.msg_cshead
	cshead.str_phone_num = app_info.phone_num
	cshead.int32_maincmd = 0x221
	head.msg_login_sig.int32_sig_type = 3
	head.msg_login_sig.bytes_sig = app_info.cookie_sig
	
	if set_user_info == None :
		set_user_info = proto_factory(0x221)
		base_info = set_user_info.user_base_info_req
		base_info.int32_uid = app_info.self_uid
		base_info.str_nickname = 'UID_' + str(app_info.self_uid)
		base_info.nGen = int(app_info.phone_num) & 0x1	# 性别按照手机最后一位进行分配
		base_info.nAget = random.randint(18, 60)
		base_info.int32_birthday_year = random.randint(1950, 2010) 
		base_info.int32_birthday_month = random.randint(1, 12)
		base_info.int32_birthday_day = random.randint(1, 28)
		base_info.str_country = u'中国'
		base_info.str_city = u'拉萨'
		base_info.str_person_desc = 'I AM GOD! %d %s' % (app_info.self_uid, datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')  )
		base_info.str_avatar = "http://zs-platform-im-avatar-images.oss-cn-hangzhou.aliyuncs.com/11006/1467790277_ff79a153196992f16b480ffeca6c107a.jpg"
		base_info.int32_height = random.randint(140, 190)
		base_info.int32_weight = random.randint(40, 90)
		base_info.str_interesting = "Flying"
		base_info.str_marriage = u"已婚" if (random.randint(0, 99) & 0x1) else u"未婚"
	else:
		set_user_info.user_base_info_req.int32_uid = app_info.self_uid

	
	#print "\n******开始设置用户资料流程******";

	data, data_len = package_format(head, set_user_info, app_info.str_cookie_key)
	if data is None:
		return 1, '0x121命令编码错误'

	tcp_sock.send(data)
	#print '-------------------->0x221 success'

	data = tcp_sock.recv(BUFFSIZE)	
	head_p, body_p = parse_package(data, 0x222, app_info.str_cookie_key)
	if head_p == None or body_p == None:
		return 2, 'set user base info parse_package'

	return_code = head_p.msg_cshead.uint32_result_code
	if return_code != 0:
		return 3, 'set user base info fail, return code = %d' % return_code

	#print '<--------------------0x222 success'
	#print "******设置用户资料成功******\n";
	
	return 0, "success";

# 客户端上送离线消息推送配置
def func_set_offline_setting(tcp_sock, app_info, push_enable, push_ring, java_switch):
	head = get_common_head()
	cshead = head.msg_cshead
	cshead.str_phone_num = app_info.phone_num
	cshead.int32_maincmd = 0x151
	head.msg_login_sig.int32_sig_type = 3
	head.msg_login_sig.bytes_sig = app_info.cookie_sig
	
	offline_push_config = proto_factory(cshead.int32_maincmd)
	offline_push_config.offline_push_enable = push_enable
	offline_push_config.is_offline_push_ring = push_ring
	offline_push_config.uint32_offline_switch_bussiness = java_switch

	
	#print "\n******开始设置客户端上送离线消息推送配置流程******";

	data, data_len = package_format(head, offline_push_config, app_info.str_cookie_key)
	if data is None:
		return 1, '%x命令编码错误' % cshead.int32_maincmd

	tcp_sock.send(data)
	#print '-------------------->0x221 success'

	data = tcp_sock.recv(BUFFSIZE)	
	head_p, body_p = parse_package(data, 0x152)
	if head_p == None :
		return 2, 'set offline setting parse_package'

	return_code = head_p.msg_cshead.uint32_result_code
	if return_code != 0:
		return 3, 'set offline setting fail, return code = %d' % return_code

	#print '<--------------------0x152 success'
	#print "******设置客户端上送离线消息推送配置******\n";
	
	return 0, "success";


def main():

	configer.initialize(sys.argv[1])

	tcp_sock = make_tcp_conn()

	'''
	#注册
	print 'start register auto_test'
	register_cmd_map = {"0xX01":0x101, "0xX02":0x102, "0xX03":0x103, "0xX04":0x104, "0xX05":0x105, "0xX06":0x106,"0xX07":0x107, "0xX08":0x108}
	re = register_and_find_passwd_process(tcp_sock, register_cmd_map)
	if re == None:
		return None
	print 'register auto_test success,uid:',self_uid	
		
	
	##密码找回	
	find_passwd_cmd_map = {"0xX01":0x101, "0xX02":0x102, "0xX03":0x303, "0xX04":0x304, "0xX05":0x305, "0xX06":0x306,"0xX07":0x307, "0xX08":0x308}
	print 'start find passwd auto_test'
	re = register_and_find_passwd_process(tcp_sock, find_passwd_cmd_map)
	if re == None:
		return None	
	print 'find passwd success'	
	'''

	#登录
	re = login_process_password(tcp_sock)
	if re == None:
		return None	


	re = login_process_cookie(tcp_sock)
	if re == None:
		return None	

	'''
	#设置用户资料	
	print 'start set user base profile'
	re = set_user_base_profile(tcp_sock)
	if re == None:
		return None
	print 'start set user base profile success'



	#获取小Z消息
	re = get_z_push_msg(tcp_sock)
	if re == None:
		return None
	print 'get СZ message success'
	#发送个人消息
	re = send_c2c_msg_process(tcp_sock)
	if re == None:
		return None
	'''
	
	re = send_c2c_msg_process(tcp_sock)
#	re = send_location_msg_process(tcp_sock)
	if re == None:
		return None
	



if __name__ == '__main__':
	main()
		
	
	
	
	
	
	

